
// const extentionEnergyMap=[
//     0,//lv0
//     0,//lv1
//     50,//lv2
//     50,//lv3
//     50,//lv4
//     50,//lv5
//     50,//lv6
//     100,//lv7
//     200,//lv8
//];

import { ExtRoom } from "../extention/room";
import { getRoomDomain } from "../global/map";

const investmentTable={
    tough: 10,
    move: 50,
    carry: 50,
    attack: 80,
    work: 100,
    ranged_attack: 150,
    heal: 250,
    claim: 600,
};
const spawnTemplates={
    // harvester:{
    //     require:[MOVE,CARRY,WORK],
    //     move: .20,
    //     carry: .40,
    //     work: .40,
    // },
    // upgrader:{
    //     require:[MOVE,CARRY,WORK],
    //     move: .40,
    //     carry: .30,
    //     work: .40,
    // },
    builder:{
        require:[MOVE,CARRY,WORK],
        move: .40,
        carry: .50,
        work: .10,
    },
    repairer:{
        require:[MOVE,WORK,CARRY],
        move: .40,
        work: .40,
        carry: .20,
    },
    attacker:{
        require:[MOVE,ATTACK],
        move: .30,
        attack: .40,
        tough: .30,
    },
    ranged_attacker:{
        require:[MOVE,RANGED_ATTACK],
        move: .30,
        ranged_attack: .50,
        tough: .20,
    },
    healer:{
        require:[MOVE,HEAL],
        move: .15,
        heal: .80,
        tough: .05,
    },
};
const countTemplates=[
    {
        // harvester:          {max: 4, min: 2, priority:0},
        // upgrader:           {max: 4, min: 2, priority:1},
        builder:            {max: 3, min: 1, priority:2},
        // attacker:           {max: 2, min: 0, priority:3},
        // ranged_attacker:    {max: 2, min: 0, priority:4},
        // healer:             {max: 1, min: 0, priority:3},
        repairer:           {max: 2, min: 1, priority:2},
    },
    {
        // harvester:          {max: 5, min: 3, priority:0},
        // upgrader:           {max: 2, min: 1, priority:1},
        builder:            {max: 2, min: 1, priority:2},
        // attacker:           {max: 2, min: 0, priority:3},
        // ranged_attacker:    {max: 2, min: 0, priority:3},
        // healer:             {max: 1, min: 0, priority:3},
        repairer:           {max: 2, min: 1, priority:2},
    },
    // {
    //     harvester:          {max: 2, min: 1, priority:0},
    //     upgrader:           {max: 2, min: 1, priority:1},
    //     builder:            {max: 2, min: 1, priority:2},
    //     attacker:           {max: 2, min: 0, priority:3},
    //     ranged_attacker:     {max: 2, min: 0, priority:3},
    //     healer:             {max: 1, min: 0, priority:3},
    //     repairer:           {max: 1, min: 1, priority:2},
    // },
];
/**
 * 
 * @param {Room|StructureSpawn} base 
 * @returns {{room:Room,spawn:StructureSpawn}}
 */
function getLegacySpawnArgs(base){
    let room;
    let spawn;
    if(base instanceof StructureSpawn){
        spawn=base;
        room=spawn.room;
    }else if (base instanceof Room){
        room=base;
        let spawns=room.find(FIND_MY_SPAWNS);
        if(spawns.length==0){
            return ERR_INVALID_TARGET;
        }
        spawn=spawns[0];
    }else{
        throw new Error("type error");
    }
    return {room:room, spawn:spawn};
}
/**@param {Room} room */
export function getMaxEnergy(room){
    return room.energyCapacityAvailable;
}
/**@param {Room} room  */
export function getMinEnergy(room){
    return room.energyAvailable;
}

export function getCreepBodyCountCoeficient(room, useRawValue = false){
    let maxCount = 50;
    let averageCreepBodyEnergy = getAverageCreepBodyCount(room);
    let ans = averageCreepBodyEnergy/(maxCount*0.85);
    if(!useRawValue && ans>1){
        ans = 1;
    }
    return ans;
}
/**
 * 
 * @param {Room} room 
 * @param {Boolean} useRawValue 
 * @returns {Number}
 */
export function getCreepActivityCoeficient(room, useRawValue = false){
    let ans = Math.sqrt(
        getCreepBodyCountCoeficient(room, true)*
        getCreepCountCoeficient(room, true));
    if(!useRawValue && ans>1){
        ans = 1;
    }
    return ans;
}
/**
 * 
 * @param {Room} room 
 * @param {Boolean} useRawValue 
 * @returns {Number}
 */
export function getCreepCountCoeficient(room, useRawValue = false){
    let maxCreepCount = room.maxCreepCount;
    let ans;
    if(!maxCreepCount || maxCreepCount === 0){
        ans = 0;
    }else{
        ans = (getCreepCount(room))/(maxCreepCount*0.85);
    }
    // console.log((getCreepCount(room)),(maxCreepCount*0.75),(maxCreepCount));
    // console.log(ans)
    // if(maxCreepCount === undefined){//todo
    //     errLog(new Error())
    // }
    if(!useRawValue && ans>1){
        ans = 1;
    }
    return ans;
}
/**
 * 
 * @param {Room} room 
 * @returns {Number}
 */
export function getCreepCount(room){
    if(room.__creepCount === undefined){
        getAverageCreepBodyCount(room);
    }
    return room.__creepCount;
}
/**
 * 
 * @param {Room} room 
 * @returns {Number}
 */
export function getAverageCreepBodyCount(room){
    if(room.__averageCreepBodyCount!==undefined){
        return room.__averageCreepBodyCount;
    }

    let mainRoom = Game.rooms[room.memory.mainRoomName];
    if(!mainRoom){
        return 0;
    }
    let totalBodyCount = 0;
    // let body;
    let creepCount = 0;
    let explorerCount = 0;
    for(let roomName of getRoomDomain(mainRoom)){
        let room = Game.rooms[roomName];
        if(!room){
            continue;
        }
        for(let creep of room.find(FIND_MY_CREEPS)){
            if(creep.memory.role === "explorer"){
                explorerCount++;
                continue;
            }
            creepCount++;
            totalBodyCount+=creep.body.length;
            // body=[];
            // for(let part of creep.body){
            //     body.push(part.type);
            // }
            // totalCount+=countEnergy(body);
        }
    }

    let averageBodyCount=totalBodyCount/creepCount;
    if(creepCount === 0){
        averageBodyCount = 0;
    }
    room.__averageCreepBodyCount = averageBodyCount;
    room.__creepCount = creepCount + explorerCount;
    return averageBodyCount;
}
/**
 * 
 * @param {Room} room 
 */
export function getApproprateEnergy(room){
    if(room.__approprateEnergy !== undefined){
        return room.__approprateEnergy;
    }
    let maxEnergy = getMaxEnergy(room);

    let ans = maxEnergy*(getCreepCountCoeficient(room)**1.5);

    if(ans >= maxEnergy){
        ans = maxEnergy;
    }else{
        ans = Math.max(Math.ceil(ans), 300);
    }

    room.__approprateEnergy = ans;
    return ans;
}

/**@param {BodyPartConstant[]|{type:BodyPartConstant}[]} body  */
export function countEnergy(body){
    if(body.length === 0){
        return 0;
    }

    if(typeof body[0] === "string"){
        return _.sum(body,(bodyPart)=>investmentTable[bodyPart]);
    }
    return _.sum(body,(bodyPart)=>investmentTable[bodyPart.type]);
}

/**
 * @param {Number} energy 
 * @param {Object} ratesMap 
 * @param {number} templateLength
 * @returns {BodyPartConstant[]}
 */
function getLimit(ratesMap, energy, templateLength=0){
    let ans=[];
    let list=[];


    let unitEnergy=0.0;
    for(let part in ratesMap){
        unitEnergy+=ratesMap[part] * investmentTable[part];
    }
    if(unitEnergy==0){
        return [];
    }

    let unitCount=energy/unitEnergy;

    if(unitCount>50-templateLength){
        unitCount=50-templateLength;
    }

    for(let part in ratesMap){
        let rate=ratesMap[part];
        let partCount=Math.floor(rate * unitCount);
        energy-=partCount * investmentTable[part];
        for(let i=0; i<partCount; i++){
            ans.push(part);
        }
        list.push(part);
    }
    list.sort((a,b)=>ratesMap[b]-ratesMap[a]);
    for(let part in list){
        let energyUse=investmentTable[part];
        while(energy>=energyUse && ans.length<50-templateLength){
            energy-=energyUse;
            ans.push(part);
        }
    }
    return ans;
}

export class SpawnCreepTemplate{
    require         = [];
    tough           = 0;
    move            = 0;
    carry           = 0;
    attack          = 0;
    work            = 0;
    ranged_attack   = 0;
    claim           = 0;
    heal            = 0;
}

/**
 * 
 * @param {Room} room 
 */
export function canSpawn(room){
    return getExtRoom(room).spawns.length > 0;
}

/**
 * 
 * @param {Room} room 
 * @param {SpawnCreepTemplate} template
 * @param {SpawnOptions | undefined} opts 
 */
export function spawnCreep(room, template, opts=undefined, maxEnergy = undefined){
    if(room === undefined){
        throw new Error("null pointer: room");
    }

    // if(!Memory.rooms[room.name].tryToSpawn){
        Memory.rooms[room.name].tryToSpawn = opts.memory.role;//log
    // }

    let spawns=ExtRoom.get(room).spawns;

    let body=[];
    body.push(...template.require);

    if(maxEnergy === undefined){
        maxEnergy=getApproprateEnergy(room);
    }

    let ratesMap={};
    for(let bodyPart in investmentTable){
        let tmp=template[bodyPart];
        if(!tmp){
            ratesMap[bodyPart]=.00;
            continue;
        }
        ratesMap[bodyPart]=tmp;
    }

    body.push(...getLimit(ratesMap, maxEnergy-countEnergy(body), body.length));

    body.sort((a,b)=>investmentTable[a]-investmentTable[b]);

    let creepName=Game.time;
    if(opts.memory && opts.memory.role){
        creepName = opts.memory.role + creepName;
    }
    let ret=ERR_INVALID_TARGET;
    for(let spawn of spawns){
        ret=spawn.spawnCreep(body,creepName,opts);
        switch(ret){
            case OK:
                console.log("spawning", room.name, body);
                return Game.creeps[creepName];
            // case ERR_INVALID_ARGS:
            // case ERR_BUSY:
            // case ERR_NOT_OWNER:
            // case ERR_NAME_EXISTS:
            // case ERR_NOT_ENOUGH_ENERGY:
            // case ERR_RCL_NOT_ENOUGH:
            default:
                continue;
        }
    }
    return ret;
}
/** @param {StructureSpawn|Room} base*/
export function getTemplates(base){
    let args=getLegacySpawnArgs(base);
    let room = args.room;

    let level=room.controller.level;
    while(level>0){
        let template=countTemplates[level-1];
        if(template){
            return template;
        }
        level--;
    }
    return countTemplates[0];
};
export const roleSpawn={
    /**@param {StructureSpawn | Room} base*/
    run:function(base, myCreeps=undefined){
        let room;
        let spawn;
        if(base instanceof StructureSpawn){
            spawn=base;
            room=spawn.room;
        }else if(base instanceof Room){
            room=base;
            let spawns=room.find(FIND_MY_SPAWNS);
            let ret=ERR_INVALID_TARGET;
            for(let spawn of spawns){
                let tmpRet=this.run(spawn);
                if(tmpRet!=OK){
                    ret=tmpRet;
                }
            }
            return ret;
        }else{
            throw new Error('base type unknown');
        }
        const templates=getTemplates(spawn);

        if(myCreeps==undefined){
            myCreeps=room.find(FIND_MY_CREEPS);
        }

        const creeps=new Map();
        var keys=[];
        
        for(var key in templates){
            creeps.set(key,myCreeps.filter(creep=>creep.memory.role==key));

            let min=0;
            if(templates[key]!=undefined){
                min=templates[key].min;
            }
            if(creeps.get(key).length<min){
                keys.push(key);
            }
        }
        //default behavior, need to disable manually in cases
        for(let i=0; i<keys.length;i++){
            switch(keys[i]){
                case 'harvester':
                case 'upgrader':
                case 'attacker':
                case 'ranged_attacker':
                case 'healer':
                    keys.splice(i,1);
                    i--;
                    break;
                default:
                    break;
            }
        }
        if(keys.length==0){
            for(var key in templates){
                let max=0;
                if(templates[key]!=undefined){
                    max=templates[key].max;
                }
                if(creeps.get(key).length<max){
                    keys.push(key);
                }
            }
            //default behavior, need to disable manually in cases
            for(let i=0; i<keys.length;i++){
                switch(keys[i]){
                    case 'harvester':
                    case 'upgrader':
                    case 'attacker':
                    case 'ranged_attacker':
                    case 'healer':
                        keys.splice(i,1);
                        i--;
                        break;
                    default:
                        break;
                }
            }
        }
        let ret=ERR_INVALID_TARGET;
        if(keys.length>0){
            let key=keys[0];

            for(let i=1; i<keys.length; i++){
                let priority=0;
                if(templates[key]!=undefined){
                    priority=templates[key].priority;
                }
                if(templates[keys[i]].priority<priority){
                    key=keys[i];
                }
            }
            
            ret=spawnCreep(room, spawnTemplates[key],{memory:{role: key}});
        }
        return ret;
    }
};